/**
 * Copyright 2010 Jake Jackson
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jake.nb.ext.ant.parser;

import java.io.IOException;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.openide.cookies.EditorCookie;
import org.openide.cookies.SaveCookie;
import org.openide.util.Exceptions;

/**
 *
 * @author JakeJ
 */
public class DocumentUtilities {

  public static Spans getCommentSpans(final Document document) {
    final Spans retVal = new Spans(document);
    final String text = getFullText(document);

    if (text.length() > 0) {
      String commentStart = "<!--";
      String commentEnd = "-->";
      int startIndex = text.indexOf(commentStart);
      while (startIndex > -1) {
        int endIndex = text.indexOf(commentEnd, startIndex + 1);
        if (endIndex > -1) {
          retVal.addSpan(new int[]{startIndex, endIndex});
          startIndex = text.indexOf(commentStart, endIndex + 1);
        } else {
          break;
        }
      }
    }

    return retVal;
  }

  /**
   * Get the span for all target definitions of the passed document, which are not contained in a comment
   * @param document the document to parse
   * @return the spans for all targets of the document
   */
  public static Spans getTargetSpans(final Document document) {
    final Spans retVal = new Spans(document);
    final Spans additionalSpans = getCommentSpans(document);
    additionalSpans.addSpans(getAntcallSpans(document));

    final String fullText = getFullText(document);
    int startIndex = indexOf(fullText, 0, additionalSpans, "<target");

    while (startIndex > -1) {
      int closingIndex = indexOf(fullText, startIndex + 1, additionalSpans, "</target>");
      final int nextStart = indexOf(fullText, startIndex + 1, additionalSpans, "<target");
      // target definition doesn't finish with "</target>" - so look for a direct close: "/>"
      if (closingIndex < 0 || (nextStart > -1 && nextStart < closingIndex)) {
        closingIndex = indexOf(fullText, startIndex + 1, additionalSpans, "/>");
        if (closingIndex > -1) {
          closingIndex += 2;
        }
      } else {
        closingIndex += 9;
      }

      if (closingIndex > -1) {
        int nameStart = indexOf(fullText, startIndex, additionalSpans, "name");
        boolean valid = false;
        if (nameStart > -1 && nameStart < closingIndex && nameStart > startIndex) {
          int firstQuote = indexOf(fullText, nameStart + 4, additionalSpans, "\"");
          if (firstQuote > -1) {
            int endQuote = indexOf(fullText, firstQuote + 1, additionalSpans, "\"");
            if (endQuote > -1) {
              final String name = fullText.substring(firstQuote + 1, endQuote);
              retVal.addSpan(new TargetSpan(document, startIndex, closingIndex, name, new Span(document, nameStart, endQuote + 1)));
              valid = true;
            }
          }
        }
        if (!valid) {
          retVal.addSpan(new TargetSpan(document, startIndex, closingIndex, "", null));
        }

        startIndex = indexOf(fullText, closingIndex + 1, additionalSpans, "<target");

      } else {
        break;
      }

    }

    return retVal;
  }

  /**
   * Gets the spans for not commented depends attributes.
   * @param document
   * @return
   */
  public static Spans getDependSpans(final Document document) {
    final Spans retVal = new Spans(document);
    final Spans commentSpans = getCommentSpans(document);
    final String text = getFullText(document);
    int dependsIndex = indexOf(text, 0, commentSpans, "depends");
    while (dependsIndex > -1) {
      // skip the first index of "
      final int closing = text.indexOf('"', text.indexOf('"', dependsIndex) + 1);
      if (closing > -1) {
        retVal.addSpan(dependsIndex, closing);
        dependsIndex = indexOf(text, closing, commentSpans, "depends");
      } else {
        break;
      }
    }
    return retVal;
  }

  /**
   * Gets the spans for non commented "antcall" tasks.
   * @param document
   * @return
   */
  public static Spans getAntcallSpans(final Document document) {
    final Spans retVal = new Spans(document);
    final Spans commentSpans = getCommentSpans(document);
    final String fullText = getFullText(document);
    int antcallIndex = indexOf(fullText, 0, commentSpans, "<antcall");
    while (antcallIndex > -1) {
      int closingIndex = indexOf(fullText, antcallIndex + 1, commentSpans, "</antcall>");
      final int nextStart = indexOf(fullText, antcallIndex + 1, commentSpans, "<antcall");
      // target definition doesn't finish with "</target>" - so look for a direct close: "/>"
      if (closingIndex < 0 || (nextStart > -1 && nextStart < closingIndex)) {
        closingIndex = indexOf(fullText, antcallIndex + 1, commentSpans, "/>");
        if (closingIndex > -1) {
          closingIndex += 2;
        }
      } else {
        closingIndex += 9;
      }

      if (closingIndex > -1) {
        boolean valid = false;
        int targetIndex = indexOf(fullText, antcallIndex, commentSpans, "target");
        if (targetIndex > -1 && targetIndex < closingIndex) {
          int firstQuote = indexOf(fullText, targetIndex + 5, commentSpans, "\"");
          if (firstQuote > -1) {
            int endQuote = indexOf(fullText, firstQuote + 1, commentSpans, "\"");
            if (endQuote > -1) {
              final String name = fullText.substring(firstQuote + 1, endQuote);
              retVal.addSpan(new AntcallSpan(document, antcallIndex, closingIndex, name,
                      new Span(document, targetIndex, endQuote + 1)));
              valid = true;
            }
          }
        }
        if (!valid) {
          retVal.addSpan(new AntcallSpan(document, antcallIndex, closingIndex, "", null));
        }
        antcallIndex = indexOf(fullText, closingIndex, commentSpans, "<antcall");


      } else {
        break;
      }
    }
    return retVal;
  }

  private static int indexOf(final String text, final int start, final Spans excluding, final String toSearch) {
    int indexOf = text.indexOf(toSearch, start);
    while (excluding.isInside(indexOf)) {
      indexOf = text.indexOf(toSearch, indexOf + 1);
    }
    return indexOf;
  }

  public static Spans getSpans(final Document document, final String string) {
    Spans retVal = new Spans(document);
    String text = getFullText(document);

    if (text.length() > 0) {
      int startIndex = text.indexOf(string);
      while (startIndex > -1) {
        retVal.addSpan(new int[]{startIndex, startIndex + string.length()});
        startIndex = text.indexOf(string, startIndex + 1);
      }
    }
    return retVal;
  }

  private static String getFullText(final Document document) {
    String retVal = null;
    try {
      retVal = document.getText(0, document.getLength());
    } catch (BadLocationException ex) {
      Exceptions.printStackTrace(ex);
    }
    return retVal == null ? "" : retVal;
  }

  /**
   * Tries to save the passed document using various ways.
   * @param document the document to save.
   */
  public static void save(final Document document) throws IOException {
    final SaveCookie cookie = NbEditorUtilities.getDataObject(document).getCookie(SaveCookie.class);
    if (cookie != null) {
      cookie.save();
    } else {
      final EditorCookie cookie1 = NbEditorUtilities.getDataObject(document).getCookie(EditorCookie.class);
      if (cookie1 != null) {
        cookie1.saveDocument();
      }
    }
  }

  private DocumentUtilities() {
  }
}
